﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Dycox.Workflows
{
    public class WorkflowForwardingContext : WorkflowExecutionContext
    {
        internal WorkflowForwardingContext(Workflow workflow, OperationArguments args)
            : base(workflow, args)
        {

        }

        internal WorkflowForwardingContext(WorkflowForwardingContext parent, WFStep step)
            : base(parent, step)
        {

        }

        public new WorkflowForwardingContext ParentContext
        {
            get { return (WorkflowForwardingContext)base.ParentContext; }
        }

        protected override IWorkflowComponent GetContextComponent()
        {
            return DataProvider.GetComponent(Arguments.ComponentID, true);
        }

        internal override void BeginExecute()
        {
            DataProvider.ValidateNoChanges();

            base.BeginExecute();

            _undo = new WorkflowUndoState(this.Process);
            _frame = CreateFrame();
        }

        private WFFrame _frame;

        internal WFFrame Frame
        {
            get { return _frame; }
        }

        private WorkflowUndoState _undo;

        internal override void EndExecute(bool submitChanges)
        {
            if (submitChanges)
            {
                DataProvider.PushFrame(_frame);
                DataProvider.FillUndoState(_undo, this);
                _frame.UndoData = new System.Data.Linq.Binary(_undo.Compress());

#if DEBUG
                if (System.IO.Directory.Exists("c:\\temp\\wfundo"))
                {
                    System.IO.File.WriteAllText(string.Format("c:\\temp\\wfundo\\{0:yyyyMMddhhmmss}.json", DateTime.Now), _undo.Serialize());
                }
#endif
            }

            base.EndExecute(submitChanges);
        }

        private WFFrame CreateFrame()
        {
            var stage = this.Stage;
            var process = this.Process;
            var step = this.Step;

            if (stage.Usage == StageUsage.Placeholder)
                throw new ArgumentException();

            if (stage.Usage == StageUsage.Entrance)
                step = null;

            WFFrame frame = process.NewFrame();

            frame.CreationTime = WorkflowManager.TimeService.Now;
            frame.Type = Arguments.FrameType;
            frame.WorkflowVersion = Workflow.CurrentVersion.ToString();
            frame.Description = GetFrameDescription(frame);
            frame.WorkflowName = Workflow.Name;
            frame.StageName = stage.Name;
            frame.StageType = stage.Usage;
            frame.StepId = Step.ID.ToString();

            return frame;
        }

        private string GetFrameDescription(WFFrame frame)
        {
            string id = "frame_desp_" + frame.Type.ToString().ToLower();

            return SR.GetString(id, Workflow.DisplayName, Stage.DisplayName);
        }

        /// <summary>
        /// Gets a value which indicates whether current context working on a newly created thread.
        /// </summary>
        public virtual bool IsNewThread
        {
            get { return false; }
        }

        /// <summary>
        /// Suspends current executing process.
        /// </summary>
        /// <remarks>
        /// <para>Caution:</para>
        /// <para>
        /// Call this method ONLY in <see cref="Stage.Exited"/> event. 
        /// Calling in other events will cause unexpected result or throw an <see cref="WorkflowException"/>.
        /// </para>
        /// </remarks>
        /// <returns>
        /// Returns true is process suspended successfully, otherwise, returns false.
        /// </returns>
        public bool SuspendProcess()
        {
            var control = this.Workflow.Control;
            var proc = this.Process;

            if (!control.IsProcessSuspended(proc, ProcessSuspendingReasons.Manual))
            {
                control.SuspendProcess(proc, ProcessSuspendingReasons.Manual);
                return true;
            }
            else
                return false;
        }

        public bool SuspendThread()
        {
            var control = this.Workflow.Control;
            var thread = this.Thread;

            if (!control.IsComponentSuspended(thread))
            {
                control.SuspendThread(thread);
                return true;
            }
            else
                return false;
        }
    }
}
